import sys
try:
    from functools import wraps
except ImportError:
    from django.utils.functional import wraps  # Python 2.4 fallback.
from django.utils.decorators import available_attrs
from django.http import Http404
from django.conf.urls.defaults import patterns
from django.core.urlresolvers import resolve

def autodiscover():
    """
    Automatically loads option definitions from options modules in
    installed apps and in the main project directory. Also makes sure
    that the options context-processor is enabled.
    """
    from django.conf import settings
    from django.utils.importlib import import_module
    from django.utils.module_loading import module_has_submodule
    
    if not 'djangooptions.context_processors.options' in settings.TEMPLATE_CONTEXT_PROCESSORS:
        settings.TEMPLATE_CONTEXT_PROCESSORS += ('djangooptions.context_processors.options',)
    for app in settings.INSTALLED_APPS:
        mod = import_module(app)
        # Attempt to import the app's options module.
        try:
            setattr(sys.modules[__name__],'_app_name',app)
            #sys.stderr.write("Importing %s.options..." % app); sys.stderr.flush();
            import_module('%s.options' % app)
            #sys.stderr.write("Done!\n"); sys.stderr.flush();
        except:
            # Decide whether to bubble up this error. If the app just
            # doesn't have an options module, we can ignore the error
            # attempting to import it, otherwise we want it to bubble up.
            #sys.stderr.write(" module not found!\n"); sys.stderr.flush();
            if module_has_submodule(mod, 'options'):
                raise
    #Attempt to find a global options module
    try:
        import_module('options')
    except:
        #sys.stderr.write("Unable to find main options module\n"); sys.stderr.flush();
        pass

def urlpatterns():
    return patterns('',(r'admin/djangooptions/option/$','djangooptions.views.option_display'),)

class OptionSet:
    def __init__(self,display_name):
        self.app_name = display_name
        
    def enable_option(self, default):
        """
        Creates an option to enable/disable access to this app via urls.
        
        Even when disabled, importing the apps models/views/options/etc.
        will still work.
        """
        from models import EnableOption
        if not EnableOption.objects.filter(app=self.app_name):
            value = EnableOption(app=self.app_name,value=default,default=("True" if default else "False"),description="Enables/Disables the app");
            value.save()
            
    def boolean_option(self,name,default,description):
        """
        Creates a boolean (true/false) option for this app.
        
        Arguments:
        name - the option's display name (a string)
        default - the option's default value (either True or False)
        description - the option's purpose (a string)
        """
        from models import BooleanOption
        if not BooleanOption.objects.filter(app=self.app_name,name=name):
            value = BooleanOption(app=self.app_name,name=name,value=default,default=("True" if default else "False"),description=description);
            value.save()
    bool_option = boolean_option
    
    def text_option(self,name,default,description):
        """
        Creates a string option for this app.
        
        Arguments:
        name - the option's display name (a string)
        default - the option's default value (a string)
        description - the option's purpose (a string)   
        """
        from models import TextOption
        if not TextOption.objects.filter(app=self.app_name,name=name):
            value = TextOption(app=self.app_name,name=name,value=default,default=default,description=description);
            value.save()
    
    def int_option(self,name,default,description):
        """
        Creates a integer option for this app.
        
        Arguments:
        name - the option's display name (a string)
        default - the option's default value (an int)
        description - the option's purpose (a string) 
        """
        from models import IntOption
        if not IntOption.objects.filter(app=self.app_name,name=name):
            value = IntOption(app=self.app_name,name=name,value=default,default=str(default),description=description);
            value.save()
    
    def float_option(self,name,default,description):
        """
        Creates a float option for this app.
        
        Arguments:
        name - the option's display name (a string)
        default - the option's default value (a float)
        description - the option's purpose (a string)
        """
        from models import FloatOption
        if not FloatOption.objects.filter(app=self.app_name,name=name):
            value = FloatOption(app=self.app_name,name=name,value=default,default=str(default),description=description);
            value.save()
    
    def choice_option(self,name,choices,default,description):
        """
        Creates a choice option for this app.
        
        Arguments:
        name - the option's display name (a string)
        choices - a list of django-compatible choices (see the django ModelField reference)
        default - the option's default value (an int index referencing one of the above choices)
        description - the option's purpose (a string)
        """
        from models import ChoiceOption
        if not ChoiceOption.objects.filter(app=self.app_name,name=name):
            value = ChoiceOption(app=self.app_name,name=name,choices=choices,value=choices[default][0],default=choices[default][1],description=description);
            value.save()
            
    def option_enabled(self,func):
        """
        Decorator to make a view automatically raise an Http404 if the app is not enabled. Usage::
            
            @option_enabled
            def my_view(request):
                #This app is enabled or has no enable option
                #...
                
        Optional Keyword Arguments:
            app - the name of the app to check. If omitted this will default
                to the current app (as long as display_name_option was set in
                this module). Otherwise raises a NameError.
        
        If the app is not enabled it raises an Http404 Not Found exception.
        Otherwise (if the app is enabled or if the app doesn't have an
        enable option) the view is called normally.
        """
        from models import BooleanOption
        def decorator(request, *args, **kwargs):
            result = BooleanOption.objects.filter(app=self.app_name,name='_enable')
            if  (self.get_option("Superuser Override",default=False,app="Options") and request.user.is_superuser) or \
                (self.get_option("Staff Override",default=False,app="Options") and request.user.is_staff) or \
                (not result or result[0].value):
                # The app is enabled or has no enable option
                return func(request, *args, **kwargs)
            raise Http404
        return wraps(func, assigned=available_attrs(func))(decorator)
    
    def is_enabled(self, default=True, app=None):
        """
        Return if the option is enabled (or default if not found).
        
        Optional Keyword Arguments:
        default - the value to return if the no enable option is found
        app - the django app that contains this option (a string)
            If this is not specified, the app that this function
            is called in is used.
        """
        if app:
            app_name = app
        else:
            app_name = self.app_name
        from models import EnableOption
        result = EnableOption.objects.filter(app=app_name)
        if result:
            return result[0].value
        else:
            return default
        
    def get_option(self,name,default=None,app=None):
        """
        Return the named option (or default if not found).
        
        Arguments:
        name - the desired option's name (a string)
        
        Optional Keyword Arguments:
        default - the value to return if the option is not found
        app - the django app that contains this option (a string)
            If this is not specified, the app that this function
            is called in is used. If the module doesn't have a
            display_name_option declared above this may not correctly
            default.
        """
        if app:
            app_name = app
        else:
            app_name = self.app_name
        from models import BooleanOption, ChoiceOption, FloatOption, IntOption, TextOption
        result = BooleanOption.objects.filter(app=app_name,name=name)
        if result: return result[0].value
        result = ChoiceOption.objects.filter(app=app_name,name=name)
        if result: return result[0].value
        result = FloatOption.objects.filter(app=app_name,name=name)
        if result: return result[0].value
        result = IntOption.objects.filter(app=app_name,name=name)
        if result: return result[0].value
        result = TextOption.objects.filter(app=app_name,name=name)
        if result: return result[0].value
        return default